
#include <linked_list.h>
#include "kramdisk.h"
#include <string.h>
#include "kmm.h"
#include "dbgio.h"
#include "process.h"



uint32_t kramdisk_read(struct vfs_node *node ,uint8_t *buffer ,uint32_t offset ,uint32_t length,uint32_t fflags){
	kramdisk_file_t *rd_file = (kramdisk_file_t *) node->inode;
	if (offset > rd_file->length)
		return 0;
	if ((offset + length) > rd_file->length)
		length = rd_file->length - offset;
	uint32_t block_id = offset / KRAMDISK_BLOCK_SIZE;
	uint32_t block_offset = offset % KRAMDISK_BLOCK_SIZE;
	uint32_t block_end_offset = block_offset + length;
	uint32_t block_read_size;
	uint32_t buffer_offset = 0;
	LLIST *block_l = kllist_get(rd_file->blocks,block_id);
	kramdisk_block_t *block = (kramdisk_block_t *) block_l->value;
	while (1){
		if (block_end_offset >= block->length)
			block_end_offset = block->length - 1;
		block_read_size = block_end_offset - block_offset;
		memcpy((void *)(((uint32_t)buffer)+buffer_offset),
			   (void *)(((uint32_t)block) + sizeof(kramdisk_block_t) + block_offset),
			   block_read_size);
		buffer_offset += block_read_size;
		if (buffer_offset == length)
			return buffer_offset;
		block_id = (offset + buffer_offset) / KRAMDISK_BLOCK_SIZE;
		block_offset = (offset + buffer_offset) % KRAMDISK_BLOCK_SIZE;
		block_end_offset = block_offset + (length - buffer_offset);
		block_l = kllist_get(rd_file->blocks,block_id);
		block = (kramdisk_block_t *) block_l->value;
	}
}

uint32_t kramdisk_write(struct vfs_node *node ,uint8_t *buffer ,uint32_t offset ,uint32_t length,uint32_t fflags){
	kramdisk_file_t *rd_file = (kramdisk_file_t *) node->inode;
	uint32_t block_id;
	uint32_t block_offset;
	uint32_t block_end_offset;
	uint32_t block_read_size;
	uint32_t buffer_offset = 0;
	LLIST *block_l;
	kramdisk_block_t *block;
	/*dprintf("Size: %i. Attempted size: %i. Must Change: %i. New Size: %i. Ptr: 0x%x.\n",
		rd_file->length,
		offset+length,
		(offset+length) > rd_file->length,
		offset+length,
		node);*/
	if ((offset+length) > rd_file->length){
		rd_file->length = offset+length;
		node->length = rd_file->length;
	}
	while (1){
		block_id = (offset + buffer_offset) / KRAMDISK_BLOCK_SIZE;
		block_offset = (offset + buffer_offset) % KRAMDISK_BLOCK_SIZE;
		block_end_offset = block_offset + (length - buffer_offset);
		block_l = kllist_get(rd_file->blocks,block_id);

		if (block_l == 0){//File has to be enlarged
			//dprintf("Enlarge\n");
			block_l = (LLIST *)kmalloc(sizeof(LLIST));//1
			block   = (kramdisk_block_t *)kmalloc(KRAMDISK_BLOCK_SIZE + sizeof(kramdisk_block_t));//2
			block_l->value = (void*) block;
			block->block_id = block_id;
			block->length = 0;
			block->offset = block_id * KRAMDISK_BLOCK_SIZE;
			llist_add_end(rd_file->blocks,block_l);
		}
		block = (kramdisk_block_t *) block_l->value;
		if (block_end_offset >= block->length){
			if (block_end_offset >= KRAMDISK_BLOCK_SIZE)
				block_end_offset =  KRAMDISK_BLOCK_SIZE - 1;
			block->length = block_end_offset + 1;//Grow block to maximal size
		}
		block_read_size = block_end_offset - block_offset;
		memcpy((void *)(((uint32_t)block) + sizeof(kramdisk_block_t)),
			   (void *)(((uint32_t)buffer)+buffer_offset),
			   block_read_size);
		buffer_offset += block_read_size;
		if (buffer_offset == length){
			//dprintf("RD length: 0x%x FS length : 0x%x\n",rd_file->length,node->length);
			return buffer_offset;}
	}
}

void kramdisk_open(struct vfs_node *node){
	return;//TODO: Add some mutex thingy
}
void kramdisk_close(struct vfs_node *node){
	return;
}

struct vfs_dir * kramdisk_readdir(struct vfs_node *node,uint32_t id){
	kramdisk_dir_t *rd_dir = (kramdisk_dir_t *) node->inode;
	LLIST *ll_f = kllist_get(rd_dir->files,id);
	if (ll_f == 0)
		return 0;
	vfs_node_t *node_f = (vfs_node_t *) ll_f->value;
	struct vfs_dir *dirent = (struct vfs_dir *)kmalloc(sizeof(vfs_dir_t));
	dirent->inode = node_f->inode;
	strcpy(dirent->name,node_f->name);
	return dirent;
}

struct vfs_node * kramdisk_finddir(struct vfs_node *node,char *name){
	kramdisk_dir_t *rd_dir = (kramdisk_dir_t *) node->inode;
	for (uint32_t current = 0;current < rd_dir->length;current++){
		LLIST *ll_f = kllist_get(rd_dir->files,current);
		if (ll_f == 0)
			return 0;
		vfs_node_t *node_f = (vfs_node_t *) ll_f->value;
		if (strcmp(node_f->name,name) == 0)
			return node_f;
	}
	return 0;
}
struct vfs_node * kramdisk_touch(struct vfs_node *node,char *name,uint32_t mode){
	tprintf("[0x%X] - _kramdisk_touch(0x%X,%s) \n",(void *)kramdisk_touch,(uint32_t)node,name);
	kramdisk_dir_t *rd_dir = (kramdisk_dir_t *) node->inode;
	kramdisk_file_t *rd_file = (kramdisk_file_t *) kmalloc(sizeof(kramdisk_file_t));
	process_info_t *p = get_current_process();
	LLIST *ll_file = (LLIST *) kmalloc(sizeof(LLIST));
	rd_file->node = (vfs_node_t *)kmalloc(sizeof(vfs_node_t));
	ll_file->value = (void *) rd_file->node;
	memset((void *)rd_file->node,0,sizeof(vfs_node_t));
	rd_file->node->inode = (uint32_t) rd_file;
	strcpy(rd_file->node->name,name);
	strcpy(rd_file->name,name);
	rd_file->node->gid = p->gid;
	rd_file->node->uid = p->uid;
	rd_file->node->mask = mode;
	rd_file->node->flags = VFS_FILE_FLAG;
	rd_file->node->parent = node;
	rd_file->node->length = 0;
	rd_file->length = 0;
	rd_file->node->read = kramdisk_read;
	rd_file->node->write = kramdisk_write;
	rd_file->node->open = kramdisk_open;
	rd_file->node->close = kramdisk_close;
	rd_file->blocks = (LLIST *) kmalloc(sizeof(LLIST));
	llist_create(rd_file->blocks);
	llist_add_end(rd_dir->files,ll_file);
	rd_dir->length++;
	tprintf(" _kramdisk_touch: return 0x%x\n node_ptr:0x%x",rd_file->node,rd_file);
	return rd_file->node;
}

struct vfs_node * kramdisk_mkdir(struct vfs_node *node,char *name,uint32_t mode){
	tprintf("[0x%X] - _kramdisk_mkdir(0x%X,%s) \n",(void *)kramdisk_mkdir,(uint32_t)node,name);
	kramdisk_dir_t *rd_dir = (kramdisk_dir_t *) node->inode;
	kramdisk_dir_t *rd_dirm = (kramdisk_dir_t *) kmalloc(sizeof(kramdisk_dir_t));
	process_info_t *p = get_current_process();
	LLIST *ll_dirm = (LLIST *) kmalloc(sizeof(LLIST));
	rd_dirm->node = (vfs_node_t *)kmalloc(sizeof(vfs_node_t));
	ll_dirm->value = (void *) rd_dirm->node;
	memset((void *)rd_dirm->node,0,sizeof(vfs_node_t));
	rd_dirm->node->inode = (uint32_t) rd_dirm;
	strcpy(rd_dirm->node->name,name);
	strcpy(rd_dirm->name,name);
	rd_dirm->node->gid = p->gid;
	rd_dirm->node->uid = p->uid;
	rd_dirm->node->mask = mode;
	rd_dirm->node->flags = VFS_DIRECTORY_FLAG;
	rd_dirm->node->parent = node;
	rd_dirm->node->length = 0;
	rd_dirm->length = 0;
	rd_dirm->node->finddir = kramdisk_finddir;
	rd_dirm->node->readdir = kramdisk_readdir;
	rd_dirm->node->touch   = kramdisk_touch;
	rd_dirm->node->mkdir   = kramdisk_mkdir;
	rd_dirm->files = (LLIST *) kmalloc(sizeof(LLIST));
	llist_create(rd_dirm->files);
	llist_add_end(rd_dir->files,ll_dirm);
	rd_dir->length++;
	tprintf(" _kramdisk_dirm: return 0x%x\n",rd_dirm->node);
	return rd_dirm->node;
}

struct vfs_node * kramdisk_mkfs(char *name){
	kramdisk_dir_t *rd_dirm = (kramdisk_dir_t *) kmalloc(sizeof(kramdisk_dir_t));
	process_info_t *p = get_current_process();
	rd_dirm->node = (vfs_node_t *)kmalloc(sizeof(vfs_node_t));
	memset((void *)rd_dirm->node,0,sizeof(vfs_node_t));
	rd_dirm->node->inode = (uint32_t) rd_dirm;
	strcpy(rd_dirm->node->name,name);
	strcpy(rd_dirm->name,name);
	rd_dirm->node->gid = p->gid;
	rd_dirm->node->uid = p->uid;
	rd_dirm->node->mask = 0x1FF;
	rd_dirm->node->flags = VFS_DIRECTORY_FLAG;
	rd_dirm->node->parent = 0;
	rd_dirm->node->length = 0;
	rd_dirm->length = 0;
	rd_dirm->node->finddir = kramdisk_finddir;
	rd_dirm->node->readdir = kramdisk_readdir;
	rd_dirm->node->touch   = kramdisk_touch;
	rd_dirm->node->mkdir   = kramdisk_mkdir;
	rd_dirm->files = (LLIST *) kmalloc(sizeof(LLIST));
	llist_create(rd_dirm->files);
	return rd_dirm->node;
}




